var _useCapture = false;

/**
 DOM_VK_BACK_SPACE : 8
 DOM_VK_TAB : 9
 DOM_VK_RETURN : 13
 DOM_VK_SPACE : 32
 DOM_VK_PAGE_UP : 33
 DOM_VK_PAGE_DOWN : 34
 DOM_VK_END : 35
 DOM_VK_HOME : 36
 DOM_VK_LEFT : 37
 DOM_VK_UP : 38
 DOM_VK_RIGHT : 39
 DOM_VK_DOWN : 40
 DOM_VK_DELETE : 46
 DOM_VK_0 ~ DOM_VK_9 : 48 ~ 57
 DOM_VK_SEMICOLON : 59 (;:)
 DOM_VK_EQUALS : 61 (=+) (+)
 DOM_VK_A ~ DOM_VK_Z : 65 ~ 90
 DOM_VK_MULTIPLY : 106 (*)
 DOM_VK_SUBTRACT : 109 (-_) (-)
 DOM_VK_DECIMAL : 110 (.)
 DOM_VK_DIVIDE : 111 (/)
 DOM_VK_COMMA : 188 (,<)
 DOM_VK_PERIOD : 190 (.>)
 DOM_VK_SLASH : 191 (/?)
 DOM_VK_BACK_QUOTE : 192 (`~)
 DOM_VK_OPEN_BRACKET : 219 ([{)
DOM_VK_BACK_SLASH : 220 (\|)
DOM_VK_CLOSE_BRACKET : 221 (]})
 DOM_VK_QUOTE : 222 ('")
 */
// 输入文字的键值
var _INPUT_KEY_MAP = _toMap('8,9,13,32,46,48..57,59,61,65..90,106,109..111,188,190..192,219..222');
// 移动光标的键值
var _CURSORMOVE_KEY_MAP = _toMap('33..40');
// 输入文字或移动光标的键值
var _CHANGE_KEY_MAP = {};
_each(_INPUT_KEY_MAP, function (key, val) {
    _CHANGE_KEY_MAP[key] = val;
});
_each(_CURSORMOVE_KEY_MAP, function (key, val) {
    _CHANGE_KEY_MAP[key] = val;
});

// add native event
function _bindEvent(el, type, fn) {
    if (el.addEventListener) {
        el.addEventListener(type, fn, _useCapture);
    } else if (el.attachEvent) {
        el.attachEvent('on' + type, fn);
    }
}

// remove native event
function _unbindEvent(el, type, fn) {
    if (el.removeEventListener) {
        el.removeEventListener(type, fn, _useCapture);
    } else if (el.detachEvent) {
        el.detachEvent('on' + type, fn);
    }
}

var _EVENT_PROPS = ('altKey,attrChange,attrName,bubbles,button,cancelable,charCode,clientX,clientY,ctrlKey,currentTarget,' +
    'data,detail,eventPhase,fromElement,handler,keyCode,metaKey,newValue,offsetX,offsetY,originalTarget,pageX,' +
    'pageY,prevValue,relatedNode,relatedTarget,screenX,screenY,shiftKey,srcElement,target,toElement,view,wheelDelta,which').split(',');

// create KEvent class
function KEvent(el, event) {
    this.init(el, event);
}

_extend(KEvent, {
    init: function (el, event) {
        var self = this, doc = el.ownerDocument || el.document || el;
        self.event = event;
        _each(_EVENT_PROPS, function (key, val) {
            self[val] = event[val];
        });
        if (!self.target) {
            self.target = self.srcElement || doc;
        }
        if (self.target.nodeType === 3) {
            self.target = self.target.parentNode;
        }
        if (!self.relatedTarget && self.fromElement) {
            self.relatedTarget = self.fromElement === self.target ? self.toElement : self.fromElement;
        }
        if (self.pageX == null && self.clientX != null) {
            var d = doc.documentElement, body = doc.body;
            self.pageX = self.clientX + (d && d.scrollLeft || body && body.scrollLeft || 0) - (d && d.clientLeft || body && body.clientLeft || 0);
            self.pageY = self.clientY + (d && d.scrollTop || body && body.scrollTop || 0) - (d && d.clientTop || body && body.clientTop || 0);
        }
        if (!self.which && ((self.charCode || self.charCode === 0) ? self.charCode : self.keyCode)) {
            self.which = self.charCode || self.keyCode;
        }
        if (!self.metaKey && self.ctrlKey) {
            self.metaKey = self.ctrlKey;
        }
        if (!self.which && self.button !== undefined) {
            self.which = (self.button & 1 ? 1 : (self.button & 2 ? 3 : (self.button & 4 ? 2 : 0)));
        }
        /**
         DOM_VK_SEMICOLON : 59 (;:)
         - IE,WEBKIT: 186
         - GECKO,OPERA : 59
         DOM_VK_EQUALS : 61 (=+)
         - IE,WEBKIT : 187
         - GECKO : 107
         - OPERA : 61
         DOM_VK_NUMPAD0 ~ DOM_VK_NUMPAD9 : 96 ~ 105
         - IE、WEBKIT,GECKO : 96 ~ 105
         - OPERA : 48 ~ 57
         DOM_VK_MULTIPLY : 106 (*)
         - IE、WEBKIT,GECKO : 106
         - OPERA : 42
         DOM_VK_ADD : 107 (+)
         - IE、WEBKIT,GECKO : 107
         - OPERA : 43
         DOM_VK_SUBTRACT : 109 (-_) (-)
         - IE,WEBKIT : 189, 109
         - GECKO : 109, 109
         - OPERA : 109, 45
         DOM_VK_DECIMAL : 110 (.)
         - IE、WEBKIT,GECKO : 110
         - OPERA : 78
         DOM_VK_DIVIDE : 111 (/)
         - IE、WEBKIT,GECKO : 111
         - OPERA : 47

         Reference:
         https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent
         http://msdn.microsoft.com/en-us/library/ms536940(v=VS.85).aspx
         */
        switch (self.which) {
            case 186 :
                self.which = 59;
                break;
            case 187 :
            case 107 :
            case 43 :
                self.which = 61;
                break;
            case 189 :
            case 45 :
                self.which = 109;
                break;
            case 42 :
                self.which = 106;
                break;
            case 47 :
                self.which = 111;
                break;
            case 78 :
                self.which = 110;
                break;
        }
        if (self.which >= 96 && self.which <= 105) {
            self.which -= 48;
        }
    },
    preventDefault: function () {
        var ev = this.event;
        if (ev.preventDefault) {
            ev.preventDefault();
        } else {
            ev.returnValue = false;
        }
    },
    stopPropagation: function () {
        var ev = this.event;
        if (ev.stopPropagation) {
            ev.stopPropagation();
        } else {
            ev.cancelBubble = true;
        }
    },
    stop: function () {
        this.preventDefault();
        this.stopPropagation();
    }
});

var _eventExpendo = 'kindeditor_' + _TIME, _eventId = 0, _eventData = {};

function _getId(el) {
    return el[_eventExpendo] || null;
}

function _setId(el) {
    el[_eventExpendo] = ++_eventId;
    return _eventId;
}

function _removeId(el) {
    try {
        delete el[_eventExpendo];
    } catch (e) {
        if (el.removeAttribute) {
            el.removeAttribute(_eventExpendo);
        }
    }
}

function _bind(el, type, fn) {
    if (type.indexOf(',') >= 0) {
        _each(type.split(','), function () {
            _bind(el, this, fn);
        });
        return;
    }
    var id = _getId(el);
    if (!id) {
        id = _setId(el);
    }
    if (_eventData[id] === undefined) {
        _eventData[id] = {};
    }
    var events = _eventData[id][type];
    if (events && events.length > 0) {
        _unbindEvent(el, type, events[0]);
    } else {
        _eventData[id][type] = [];
        _eventData[id].el = el;
    }
    events = _eventData[id][type];
    if (events.length === 0) {
        events[0] = function (e) {
            var kevent = e ? new KEvent(el, e) : undefined;
            _each(events, function (i, event) {
                if (i > 0 && event) {
                    event.call(el, kevent);
                }
            });
        };
    }
    if (_inArray(fn, events) < 0) {
        events.push(fn);
    }
    _bindEvent(el, type, events[0]);
}

function _unbind(el, type, fn) {
    if (type && type.indexOf(',') >= 0) {
        _each(type.split(','), function () {
            _unbind(el, this, fn);
        });
        return;
    }
    var id = _getId(el);
    if (!id) {
        return;
    }
    if (type === undefined) {
        if (id in _eventData) {
            _each(_eventData[id], function (key, events) {
                if (key != 'el' && events.length > 0) {
                    _unbindEvent(el, key, events[0]);
                }
            });
            delete _eventData[id];
            _removeId(el);
        }
        return;
    }
    if (!_eventData[id]) {
        return;
    }
    var events = _eventData[id][type];
    if (events && events.length > 0) {
        if (fn === undefined) {
            _unbindEvent(el, type, events[0]);
            delete _eventData[id][type];
        } else {
            _each(events, function (i, event) {
                if (i > 0 && event === fn) {
                    events.splice(i, 1);
                }
            });
            if (events.length == 1) {
                _unbindEvent(el, type, events[0]);
                delete _eventData[id][type];
            }
        }
        var count = 0;
        _each(_eventData[id], function () {
            count++;
        });
        if (count < 2) {
            delete _eventData[id];
            _removeId(el);
        }
    }
}

function _fire(el, type) {
    if (type.indexOf(',') >= 0) {
        _each(type.split(','), function () {
            _fire(el, this);
        });
        return;
    }
    var id = _getId(el);
    if (!id) {
        return;
    }
    var events = _eventData[id][type];
    if (_eventData[id] && events && events.length > 0) {
        events[0]();
    }
}

function _ctrl(el, key, fn) {
    var self = this;
    key = /^\d{2,}$/.test(key) ? key : key.toUpperCase().charCodeAt(0);
    _bind(el, 'keydown', function (e) {
        if (e.ctrlKey && e.which == key && !e.shiftKey && !e.altKey) {
            fn.call(el);
            e.stop();
        }
    });
}

var _readyFinished = false;

function _ready(fn) {
    if (_readyFinished) {
        fn(KindEditor);
        return;
    }
    var loaded = false;

    function readyFunc() {
        if (!loaded) {
            loaded = true;
            fn(KindEditor);
            _readyFinished = true;
        }
    }

    function ieReadyFunc() {
        if (!loaded) {
            try {
                document.documentElement.doScroll('left');
            } catch (e) {
                setTimeout(ieReadyFunc, 100);
                return;
            }
            readyFunc();
        }
    }

    function ieReadyStateFunc() {
        if (document.readyState === 'complete') {
            readyFunc();
        }
    }

    if (document.addEventListener) {
        _bind(document, 'DOMContentLoaded', readyFunc);
    } else if (document.attachEvent) {
        _bind(document, 'readystatechange', ieReadyStateFunc);
        // 在跨域的frame里调用会报错
        var toplevel = false;
        try {
            toplevel = window.frameElement == null;
        } catch (e) {
        }
        if (document.documentElement.doScroll && toplevel) {
            ieReadyFunc();
        }
    }
    _bind(window, 'load', readyFunc);
}

/**
 Note:
 发现绑定dbclick事件后移除element会有内存泄漏，以下代码也不起作用。
 Reference:
 http://isaacschlueter.com/2006/10/msie-memory-leaks/
 http://msdn.microsoft.com/en-us/library/bb250448.aspx
 */
if (window.attachEvent) {
    window.attachEvent('onunload', function () {
        _each(_eventData, function (key, events) {
            if (events.el) {
                _unbind(events.el);
            }
        });
    });
}

K.ctrl = _ctrl;
K.ready = _ready;
